/************************************************
 * MirrorOS Kernel Drivers
 * Fat FS Driver
 * Copyright 2008 Matthew Iselin
 ***********************************************/

// Includes
#include "fat.h"

// Functions

// we need this in this file because it's so big
int fat_internal_sys_read_file( struct fat_fs fs, struct intdesc desc, char* dest, int count )
{
	// to make life easy, we set some stuff up here first
	unsigned int offset = desc.desc_offset;
	unsigned int file_first_clus = desc.dir_entry.DIR_FstClusLO | (desc.dir_entry.DIR_FstClusHI << 16);
	
	// special case: where there is a zero cluster this means that the file has been created but no data has been written
	if( file_first_clus == 0 )
		return -1;
	
	// we need to firsly check for a sector boundary break
	
	// the final offset into the file
	unsigned int finalbyte = offset + count;
	
	// if we're about to read past the EOF, make sure that we handle it properly
	if( finalbyte > desc.dir_entry.DIR_FileSize )
		finalbyte = desc.dir_entry.DIR_FileSize;
	
	// the sector of the offset and the final byte
	unsigned int offsect = offset / fs.bpb.BPB_BytsPerSec;
	unsigned int finalsect = finalbyte / fs.bpb.BPB_BytsPerSec;
	
	// number of bytes read so far
	unsigned int nb = 0;

	// open the mount point
	desc.desc = fs.desc = sys_open( fs.mount, 0 );
	if( desc.desc == -1 )
		return -1;
	
	// sector buffer
	char* sector = (char*) malloc( fs.bpb.BPB_BytsPerSec * fs.bpb.BPB_SecPerClus );
	
	// calculate the initial cluster to start reading from and the cluster to read to
	unsigned int firstclus = file_first_clus + (offsect / fs.bpb.BPB_SecPerClus);
	unsigned int lastclus = file_first_clus + (finalsect / fs.bpb.BPB_SecPerClus);
	
	// get the offset within the first and last cluster of the first sector to read
	unsigned int firstclus_offset = offset % (fs.bpb.BPB_BytsPerSec * fs.bpb.BPB_SecPerClus);
	unsigned int lastclus_offset = finalbyte % (fs.bpb.BPB_BytsPerSec * fs.bpb.BPB_SecPerClus);
	
	// calculate the number of clusters to read
	unsigned int numclusters = (lastclus - firstclus) + 1;
	
	// read in the data
	unsigned int i, cluster_entry = firstclus;
#if DEBUG
	dprintf( "numclusters = %d\n", numclusters );
#endif
	for( i = 0; i < numclusters; i++ )
	{		
		// get the first sector number of this cluster
		int sec_num = first_sector_of_cluster( fs, cluster_entry );
		int last_sec = sec_num + fs.bpb.BPB_SecPerClus;
		
		// read in the cluster
		sys_seek( desc.desc, sec_num, SEEK_SET );
		int ret = sys_read( desc.desc, sector, fs.bpb.BPB_SecPerClus );
		
		// can we read in the entire cluster?
		if( i != 0 && (i + 1) != numclusters )
		{
			int z;
			for( z = 0; z < (fs.bpb.BPB_BytsPerSec * fs.bpb.BPB_SecPerClus); z++ )
				dest[nb++] = sector[z];
		}
		// or should we read partially?
		else
		{
			// read from the first cluster offset we found above
			if( i == 0 )
			{
#if DEBUG
				dprintf( "count = %d\n", count );
				dprintf( "%d bytes per cluster\n", fs.bpb.BPB_BytsPerSec * fs.bpb.BPB_SecPerClus );
#endif
				unsigned int int_count = finalbyte; // - firstclus_offset;
				if( (int_count - firstclus_offset) > count )
					int_count = firstclus_offset + count;
				if( int_count >= (fs.bpb.BPB_BytsPerSec * fs.bpb.BPB_SecPerClus) )
					int_count = (fs.bpb.BPB_BytsPerSec * fs.bpb.BPB_SecPerClus);
#if DEBUG
				dprintf( "reading from %d to %d [%d bytes, %d]\n", firstclus_offset, int_count, int_count - firstclus_offset, count );
#endif
				int z;
				for( z = firstclus_offset; z < int_count; z++ )
					dest[nb++] = sector[z];
			}
			// read up until the last cluster offset we found above
			else if( (i + 1) == numclusters )
			{
				unsigned int int_count = lastclus_offset;
				if( int_count >= (fs.bpb.BPB_BytsPerSec * fs.bpb.BPB_SecPerClus) )
					int_count = (fs.bpb.BPB_BytsPerSec * fs.bpb.BPB_SecPerClus);
				int z;
				for( z = 0; z < (int_count); z++ )
					dest[nb++] = sector[z];
			}
		}
		
		// get the next cluster entry
		cluster_entry = get_clus_entry( fs, cluster_entry );
	}
	
	// free the sector buffer
	free( sector );
	
	// close the device again
	sys_close( desc.desc );
	
	// return the number of bytes read
	return nb;
}
